/*=============================================*/
typedef  unsigned  char uchar;   //将unsigned char数据类型简写成uchar
/*=============================================*/
int RST = 2;  //引脚名称定义
int DAT = 3;  //引脚名称定义
int CLK = 4;  //引脚名称定义
/*=============================================*/
//DS1302读数据
//cmd 读寄存器指令
uchar DS1302_Read_Data(uchar cmd)
{
  //确保时序一开始都是从低电平开始的
  digitalWrite(RST,0);
  digitalWrite(CLK,0);
  //开始写时序操作（上使能）
  digitalWrite(RST,1);
  //写cmd数据
  //定义DAT引脚位输出模式，用于写数据
  pinMode(DAT,OUTPUT);
  for(uchar i=0; i<8; i++)
  {
    bool cmd_bit = 0;           //bool类型的数据只有0和1，用于带入到digitalWrite中
    digitalWrite(CLK,0);        //再一次确保CLK引脚是低电平的
    cmd_bit = cmd & 0x01;       //cmd数据位分解，用按位与的方式，提取出每一位二进制数
    digitalWrite(DAT,cmd_bit);  //将DAT引脚置高，相当于发了1，置低发0；
    digitalWrite(CLK,1);        //CLK引脚置高，DAT的位数据才能传入到DS1302
    cmd >>= 1;                  //cmd数据值右移一位
  }
  //读data数据
  //定义DAT引脚位输入模式，用于读数据
  pinMode(DAT,INPUT);
  uchar data = 0;  //定义data变量，用来存储读到的数据
  for(uchar i=0; i<8; i++)
  {
    digitalWrite(CLK,0);      //CLK置低，产生下降沿才能读数据，这一句执行了，下面就可以把DAT读进来
    data >>= 1;              //data数据值右移一位，注意！！这个右移一定不能放在下面if块中，或者if语句之后。因为最后一次循环结束后，还会再右移一位，这时候数据就是错误的。还要注意的是这里接收的值是bcd码，在主程序中再从bcd码转成普通数据，如果这时候这条指令放在if后面，输出的数据也就会奇怪了；就好比分钟设了25分，结果输出是12分，这就是data>>=1位置放错而导致的一系列错误
    bool data_bit = digitalRead(DAT);
    if(data_bit)      //判断DAT有没有获取到高电平,也就是数据1
      data = data | 0x80;    //数据是从最低位开始读的，最低位最后是在最右边。所以如果最低位为高电平，那么会被按位或运算0x80=0b10000000，把最高位置1，后面再右移1位，最后8个位读完，最低位也就到了最右边了；如果这个判断不执行，DAT就是0，那么直接跳过判断右移，相当于把最高位置0
    digitalWrite(CLK,1);     //CLK置1，回到高电平，以便下一次循环产生下降沿而读取数据
  }
  return data;  //返回读到的数据
}
 
//DS1302写数据
//cmd 写寄存器指令
//data 要写入的数据
void DS1302_Write_Data(uchar cmd, uchar data)
{
  //确保时序一开始都是从低电平开始的
  digitalWrite(RST,0);
  digitalWrite(CLK,0);
  //开始执行写时序操作
  digitalWrite(RST,1);
  //定义DAT引脚为输出模式，用于写数据
  pinMode(DAT,OUTPUT);
  //写cmd数据
  for(uchar i=0; i<8; i++)  //每次写一位，循环8次，写一个字节的数据
  {
    bool cmd_bit = 0;       //定义一个bool变量用来存储分解后的位数据
    digitalWrite(CLK,0);    //CLK引脚设置电平位0，用以后面置1时能够产生上升沿
    cmd_bit = cmd & 0x01;   //cmd数据位分解，用按位与的方式，提取出每一位二进制数
    digitalWrite(DAT,cmd_bit); //从cmd数据的最低位开始发送
    digitalWrite(CLK,1);         //CLK置高电平，DAT上的数据0或1就发送出去了
    cmd >>= 1;               //cmd数据值右移一位
  }
  //写data数据
  for(uchar i=0; i<8; i++)   //每次写一位，循环8次，写一个字节的数据
  {
    bool data_bit = 0;          //定义一个bool变量用来存储分解后的位数据
    digitalWrite(CLK,0);        //CLK引脚设置电平位0，用以后面置1时能够产生上升沿
    data_bit = data & 0x01;     //cmd数据位分解，用按位与的方式，提取出每一位二进制数
    digitalWrite(DAT,data_bit); //从cmd数据的最低位开始发送
    digitalWrite(CLK,1);        //CLK置高电平，DAT上的数据0或1就发送出去了
    data >>= 1;                 //data数据值右移一位
  }
}
 
//数据转BCD码
//十进制转BCD码，就相当于把这个十进制数看成十六进制，然后再转换成二进制数，十六进制的10相当于十进制的16；所以后面求出来的商要乘16
uchar data_bcd(uchar data)
{
  uchar data1 = data/10;
  uchar data2 = data%10;
  uchar bcd = data1*16 + data2;
  return bcd;
}
 
//BCD码转数据
//十进制转BCD码，就相当于把这个十进制数看成十六进制，然后再转换成二进制数，十六进制的10相当于十进制的16；所以后面求出来的商要乘16
uchar bcd_data(uchar bcd)
{
  uchar bcd1 = bcd/16;
  uchar bcd2 = bcd%16;
  uchar data = bcd1*10 + bcd2;
  return data;
}
 
 
/*=============================================*/
 
 
void setup()
{
  Serial.begin(9600);  //串口初始化，波特率为9600
  pinMode(RST,OUTPUT);  //引脚初始化，RST为输出模式
  pinMode(DAT,OUTPUT);  //引脚初始化，DAT为输出模式
  pinMode(CLK,OUTPUT);  //引脚初始化，CLK为输出模式
 
  // //设置DS1302时钟日历寄存器
  // //关闭写保护，0x8e是DS1302中寄存器的写保护寄存器地址，置0，则关闭写保护；读的是0x8f
  // DS1302_Write_Data(0x8e,0);
  
  // //写秒，秒的写寄存器是0x80，秒的读寄存器是0x81；由于DS1302的写入的数据都是BCD码，需要先用data_bcd()函数对普通的十进制数进行处理，返回后的值再代入 DS1302_Write_Data 函数中
  // DS1302_Write_Data(0x80,data_bcd(35));   //30秒
  // //写分钟，分钟的写寄存器是0x82，分钟的读寄存器是0x83；由于DS1302的写入的数据都是BCD码，需要先用data_bcd()函数对普通的十进制数进行处理，返回后的值再代入 DS1302_Write_Data 函数中
  // DS1302_Write_Data(0x82,data_bcd(50));  //15分
  // //写小时，小时的写寄存器是0x84，小时的读寄存器是0x85；由于DS1302的写入的数据都是BCD码，需要先用data_bcd()函数对普通的十进制数进行处理，返回后的值再代入 DS1302_Write_Data 函数中
  // DS1302_Write_Data(0x84,data_bcd(8));  //19时
 
  // //开启写保护，0x8e是DS1302中寄存器的写保护寄存器地址，最高位置1，也就是0x80，则开启写保护；读的是0x8f
  // DS1302_Write_Data(0x8e,0x80);
}
 
void loop()
{
  //定义 秒、分、时 三个变量
  uchar second, minute, hour;
  while(1)                                        //无限循环
  {
    DS1302_Write_Data(0x8e,0);                    //关闭写保护
    second = bcd_data(DS1302_Read_Data(0x81));    //读取当前秒，DS1302_Read_Data(0x81)返回的值是BCD码，要转化成普通的十进制数才看得懂，外面再套上bcd_data函数
    minute = bcd_data(DS1302_Read_Data(0x83));    //读取当前分，DS1302_Read_Data(0x83)返回的值是BCD码，要转化成普通的十进制数才看得懂，外面再套上bcd_data函数
    hour = bcd_data(DS1302_Read_Data(0x85));      //读取当前时，DS1302_Read_Data(0x85)返回的值是BCD码，要转化成普通的十进制数才看得懂，外面再套上bcd_data函数
    DS1302_Write_Data(0x8e,0x80);                 //打开写保护
    Serial.print(hour);                          //打印当前秒
    Serial.print(":");                           //打印分隔符
    Serial.print(minute);                        //打印当前分
    Serial.print(":");                           //打印分隔符
    Serial.println(second);                      //打印当前时，并且println比print多了'\n'换行符,
    delay(1000);                                  //延时1秒
  }
}